package com.chen.proxyProvider;

import com.chen.util.IPProxyProvider;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.proxy.Proxy;
import us.codecraft.webmagic.proxy.ProxyProvider;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class UpdateProxyProvider implements ProxyProvider {

    private List<Proxy> proxies = null;

    private final Map<Integer, Integer> proxyInfo = new ConcurrentHashMap<>();

    private final AtomicInteger pointer;

    private final BlockingQueue<Proxy> freeProxies = new LinkedBlockingQueue<>();

    private final Map<Integer, Integer> proxySite = new ConcurrentHashMap<>();

    private Properties properties = null;

    private Lock lock = new ReentrantLock();

    public UpdateProxyProvider(Integer proxyNumber, Properties properties) {
        this.properties = properties;
        IPProxyProvider.setProperties(properties);
        pointer = new AtomicInteger(-1);
        String build = new UrlFormat(UrlFormat.ZMPROXY).setNum(proxyNumber)
                .setTime(Integer.parseInt(properties.getProperty("time")))
                .setType(Integer.parseInt(properties.getProperty("type")))
                .setPort(Integer.parseInt(properties.getProperty("port")))
                .build();
        proxies = IPProxyProvider.jsonToProxys(IPProxyProvider.getResource(build));
        for (Proxy proxy : proxies) {
            proxySite.put(proxy.hashCode(), proxyInfo.size());
            proxyInfo.put(proxy.hashCode(), 0);
        }
    }


    @Override
    public void returnProxy(Proxy proxy, Page page, Task task) {
        if (!page.isDownloadSuccess()) {
            Integer failNumber = proxyInfo.get(proxy.hashCode());
            failNumber++;
            if (failNumber >= 20) {
                Integer site = proxySite.get(proxy.hashCode());
                if (freeProxies.size() > 0) {
                    Proxy poll = freeProxies.poll();
                    proxies.set(site, poll);
                    proxyInfo.put(poll.hashCode(), 0);
                    proxySite.put(poll.hashCode(), site);
                } else {
                    lock.lock();
                    try {
                        if (freeProxies.size() == 0) {
                            String build = new UrlFormat(UrlFormat.ZMPROXY).setNum(5)
                                    .setTime(Integer.parseInt(properties.getProperty("time")))
                                    .setType(Integer.parseInt(properties.getProperty("type")))
                                    .setPort(Integer.parseInt(properties.getProperty("port")))
                                    .build();

                            List<Proxy> resource = IPProxyProvider.jsonToProxys(IPProxyProvider.getResource(build));
                            assert resource != null;
                            freeProxies.addAll(resource);
                        }
                    } finally {
                        lock.unlock();
                    }
                    Proxy poll = freeProxies.poll();
                    proxies.set(site, poll);
                    proxyInfo.put(poll.hashCode(), 0);
                    proxySite.put(poll.hashCode(), site);
                }

            }
            proxyInfo.put(proxy.hashCode(), failNumber);
        } else {
            proxyInfo.put(proxy.hashCode(), 0);
        }
    }

    @Override
    public Proxy getProxy(Task task) {
        return proxies.get(incrForLoop());
    }

    private int incrForLoop() {
        int p = pointer.incrementAndGet();
        int size = proxies.size();
        if (p < size) {
            return p;
        }
        while (!pointer.compareAndSet(p, p % size)) {
            p = pointer.get();
        }
        return p % size;
    }
}
